package lnet

import (
	"errors"
	"fmt"
	"io"
	"linx/liface"
	"linx/utils"
	"net"
	"sync"
)

// 连接模块
type Connection struct {
	// 属于的Server
	TcpServer liface.IServer
	// 属性
	Conn *net.TCPConn
	// 连接id
	ConnID uint32
	// 是否关闭
	IsClosed bool
	// 退出管道
	ExitChan chan bool
	// 消息管道
	msgChan chan []byte
	// 消息处理函数
	MsgHandler liface.IMsgHandler
	// 链接属性集合
	property map[string]interface{}
	// 保护链接属性的锁
	propertyLock sync.RWMutex
}

// 新建客户端链接模块的方法
func NewConnection(server liface.IServer, conn *net.TCPConn, connID uint32, msgHandler liface.IMsgHandler) *Connection {
	c := &Connection{
		TcpServer:  server,
		Conn:       conn,
		ConnID:     connID,
		IsClosed:   false,
		ExitChan:   make(chan bool, 1),
		msgChan:    make(chan []byte),
		MsgHandler: msgHandler,
		property: make(map[string]interface{}),
	}
	// 将conn加入ConnManager中
	c.TcpServer.GetConnMgr().Add(c)

	return c
}

func (c *Connection) StartWriter() {
	fmt.Println("Writer Goroutine is running")
	defer fmt.Println("connID = ", c.ConnID, " Writer is exit, remote addr is ", c.Conn.RemoteAddr())
	defer c.Stop()

	// 不断阻塞的等待 channel 的消息，进行写给客户端
	for {
		select {
		case data := <-c.msgChan:
			if _, err := c.Conn.Write(data); err != nil {
				fmt.Println("Send data error:", err.Error())
				return
			}
		case <-c.ExitChan:
			// 代表Reader已经退出，此时 Writer也要退出
			return
		}
	}
}

func (c *Connection) StartReader() {
	fmt.Println("Reader Goroutine is running...")
	defer fmt.Println("connID = ", c.ConnID, " Reader is exit, remote addr is ", c.Conn.RemoteAddr())
	defer c.Stop()

	for {
		// 创建一个拆包对象
		dp := NewDataPack()

		// 读取客户端的 msg head
		headData := make([]byte, dp.GetHeadLen())
		if _, err := io.ReadFull(c.GetTCPConnection(), headData); err != nil {
			fmt.Println("read msg head err:", err.Error())
			break
		}

		// 得到 data len
		msg, err := dp.Unpack(headData)
		if err != nil {
			fmt.Println("unpack head err:", err.Error())
			break
		}
		var data []byte
		if msg.GetMsgLen() > 0 {
			data = make([]byte, msg.GetMsgLen())
			if _, err := io.ReadFull(c.GetTCPConnection(), data); err != nil {
				fmt.Println("read msg data err:", err.Error())
				break
			}
		}
		msg.SetMsgData(data)
		req := Request{
			conn: c,
			msg:  msg,
		}
		if utils.GlobalObject.WorkPoolSize > 0 {
			// 已经开启了工作流机制，将消息发送给worker工作池处理
			c.MsgHandler.SendMsgTaskQueue(&req)
		} else {
			// 执行注册的路由方法
			go c.MsgHandler.DoMsgHandler(&req)
		}

	}
}

// 提供客户端的封包功能
func (c *Connection) SendMsg(msgId uint32, data []byte) error {
	if c.IsClosed {
		return errors.New("Connection is closed when send msg!")
	}

	// 将data进行封包 MsgDataLen|MsgId|Data
	dp := NewDataPack()
	binaryMsg, err := dp.Pack(NewMsgPackage(msgId, data))
	if err != nil {
		fmt.Println("pack data err:", err.Error())
		return errors.New("pack data error")
	}
	// 将数据发送给客户端
	c.msgChan <- binaryMsg

	return nil
}

// 开启连接
func (c *Connection) Start() {
	fmt.Println("Conn Start() ... ConnID = ", c.ConnID)
	// 启动从当前连接的读写数据的业务
	go c.StartReader()
	go c.StartWriter()

	// 安装开发者传递进来的 创建链接之后需要调用的处理业务 执行对应 Hook函数
	c.TcpServer.CallOnConnStart(c)
}

// 停止连接 结束当前连接的工作
func (c *Connection) Stop() {
	fmt.Println("Conn Stop() ... ConnID = ", c.ConnID)

	// 如果当前已经关闭，则直接退出
	if c.IsClosed {
		return
	}
	c.IsClosed = true

	// 调用开发者注册的 销毁链接之前 需要执行的业务 Hook 函数
	c.TcpServer.CallOnConnStop(c)

	if err := c.Conn.Close(); err != nil {
		fmt.Println("c.Conn.Close() err:", err.Error())
		c.IsClosed = false
	}
	// 告知Writer关闭
	c.ExitChan <- true

	// 将当前连接从ConnMgr中删除
	c.TcpServer.GetConnMgr().Remove(c)

	close(c.ExitChan)
	close(c.msgChan)
}

// 获取当前连接的绑定 socket conn
func (c *Connection) GetTCPConnection() *net.TCPConn {
	return c.Conn
}

// 获取当前连接模块的连接 ID
func (c *Connection) GetConnID() uint32 {
	return c.ConnID
}

// 获取远程客户端的 TCP 状态 IP Port
func (c *Connection) GetRemoteAddr() net.Addr {
	return c.Conn.RemoteAddr()
}

// 设置链接属性
func (c *Connection) SetProperty(key string, value interface{}) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()

	c.property[key] = value
}

// 获取链接属性
func (c *Connection) GetProperty(key string) (interface{}, error) {
	c.propertyLock.RLock()
	defer c.propertyLock.RUnlock()
	if value, ok := c.property[key]; ok {
		return value, nil
	}
	return nil, errors.New("Property not found!")
}

// 移除链接属性
func (c *Connection) RemoveProperty(key string) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()

	delete(c.property, key)
}
